/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.base.tree;

import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.webos.java.base.Frame;
import org.webos.java.root.Log;

/**
 *
 * @author Kostya
 */
public class TreeController <T extends TreeControllerItem>{
    private TreeControllerItem<Object, T> root = null;
    private TreeControllerItem selected = null;
    private List<TreeSelectionListener> treeSelectionList = new ArrayList();
    private TreeControllerItemProvider itemProvider = null;
    private Frame frame = null;
    private boolean silentMode = false;
    private boolean autoMoveFocus = true;

    public TreeController(Frame aFrame) {
        this.frame = aFrame;
        frame.addHandler("getTree", this, "getTreeH");
        frame.addHandler("selectionChanged", this, "selectionChangedH");
        frame.addHandler("setState", this, "setStateH");
        root = new TreeControllerItem<Object, T>(this, null) {
            @Override
            public void objectTOJSON(JSONObject aObject) {
            }
        };
        root.setId("$tree$root$id$");
        root.setCollapsed(false);
    }

    public T getChild(int pos){
        return (T) root.getChildren(pos);
    }

    public int getChildrenCount(){
        return root.getChildrenCount();
    }

    public TreeControllerItem findItemByID(String aID){
        return findItemByID(aID, getRoot());
    }

    public TreeControllerItem findAllItemByID(String aID){
        return findAllItemByID(aID, getRoot());
    }

    public TreeControllerItem findAllItemByID(String aID, TreeControllerItem aItem){
        if(aItem.getId()!=null && aItem.getId().equalsIgnoreCase(aID))
            return aItem;
        for (Object object : aItem.allchildren) {
            TreeControllerItem item = (TreeControllerItem) object;
            TreeControllerItem result = findAllItemByID(aID, item);
            if(result!=null)
                return result;
        }
        return null;
    }

    public TreeControllerItem findItemByID(String aID, TreeControllerItem aItem){
        if(aItem.getId()!=null && aItem.getId().equalsIgnoreCase(aID))
            return aItem;
        for (Object object : aItem.children) {
            TreeControllerItem item = (TreeControllerItem) object;
            TreeControllerItem result = findItemByID(aID, item);
            if(result!=null)
                return result;
        }
        return null;
    }

    public void setSelectedItem(TreeControllerItem aItem){
        setSelectedItem(aItem, true);
    }
    
    public void setSelectedItem(TreeControllerItem aItem, boolean announce){
        if(aItem==null){
            Log.debug(null, "setSelectedItem deselect all");
            selected = null;
            notifySelectionChanged(announce);
        }else{
            Log.debug(null, "setSelectedItem select "+aItem.getId());
            selected = findItemByID(aItem.getId());
            Log.debug(null, "setSelectedItem selected "+selected);
            notifySelectionChanged(announce);
        }
    }

    private JSONObject treeFragmentToJSON(TreeControllerItem aItem){
        try {
            JSONObject obj = treeItemToJSON(aItem);
//            if(aItem.isCollapsed())
//                return obj;
            JSONArray arr = new JSONArray();
            for (Object object : aItem.children) {
                TreeControllerItem item = (TreeControllerItem) object;
                if(item.isVisible())
                    arr.put(treeFragmentToJSON(item));
            }
            obj.put("children", arr);
            return obj;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    //From client
    public JSONObject getTree(){
        try {
            JSONObject tree = treeFragmentToJSON(getRoot());
            if(getSelected()!=null)
                tree.put("selected", treeItemToJSON(getSelected()));
//            Log.debug(this, "Send tree: "+tree);
            return tree;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    //From client
    public void selectionChanged(String aID){
        setSelectedItem(findItemByID(aID,getRoot()), false);
    }

    //From client
    public void setState(String aID, boolean aCollapsed){
        TreeControllerItem aItem = findItemByID(aID,getRoot());
        if(aItem!=null){
            aItem.setCollapsed(aCollapsed);
            Log.debug(this, "Set state of "+aItem.getId()+" to "+aCollapsed);
        }
    }

    private JSONObject treeItemToJSON(TreeControllerItem aItem){
        try {
            JSONObject object = new JSONObject();
            object.put("id", aItem.getId());
            object.put("collapsed", aItem.isCollapsed());
            object.put("type", aItem.getType());
            object.put("icon", "");
            if(aItem.getData()!=null)
                object.put("data", aItem.getData());
            if(aItem.getParent()!=null){
                int position = aItem.getParent().children.indexOf(aItem);
                object.put("position", position);
            }
            aItem.objectTOJSON(object);
            return object;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return new JSONObject();
    }

    //From client
    public JSONObject loadItems(String aID){
        try {
            TreeControllerItem item = findItemByID(aID,getRoot());
            if(item==null)
                return null;
            if(itemProvider==null)
                return null;
            TreeControllerItem[] items = itemProvider.loadItems(item);
            item.children.clear();

            JSONObject object = new JSONObject();
            JSONArray arr = new JSONArray();
            for (TreeControllerItem child : items) {
                child.setParent(item);
                item.children.add(child);
                arr.put(treeItemToJSON(child));
            }
            object.put("items", arr);
            setSelectedItem(getSelected(), true);
            item.setCollapsed(false);
            return object;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    private void notifySelectionChanged(boolean sendMessage){
        for (TreeSelectionListener listener : treeSelectionList) {
            listener.selectionChanged(getSelected());
        }
        if(!sendMessage)
            return;
        //Send message
        try {
            JSONObject object = new JSONObject();
            if(getSelected()!=null)
                object.putOpt("selected", treeItemToJSON(getSelected()));
            Log.debug(this, "notifySelectionChanged: "+object);
            frame.sendMessage("treeItemSelected", object);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    public void addTreeSelectionListener(TreeSelectionListener aListener){
        treeSelectionList.add(aListener);
    }

    public void removeTreeSelectionListener(TreeSelectionListener aListener){
        treeSelectionList.remove(getRoot());
    }

    public void setSilentMode(){
        silentMode = true;
    }

    public void sendTree(){
        silentMode = false;
        try {
            frame.sendMessage("newTree", getTree());
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    public void setElementState(TreeControllerItem aItem, boolean aState){
        if(aItem.isCollapsed()==aState)
            return;
        try {
            aItem.setCollapsed(aState);
            if(!aItem.isCollapsed() && aItem.getType()==TreeControllerItem.TYPE_UNKNOWN){
                loadItems(aItem.getId());
            }
            if(silentMode)
                return;
            JSONObject obj = treeItemToJSON(aItem);
            obj.put("id", aItem.getId());
//            obj.put("state", aState);
            frame.sendMessage("changeTreeItemState", obj);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    void itemInserted(TreeControllerItem aItem){
        if(silentMode)
            return;
        Log.debug(this, "New item ready to insert");
        if(aItem.getParent()==null)
            return;
        try {
            JSONObject obj = treeFragmentToJSON(aItem);
            obj.put("parentID", aItem.getParent().getId());
            frame.sendMessage("treeItemInserted", obj);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    void itemRemoved(TreeControllerItem aItem, int pos){
        if(silentMode)
            return;
        try {
            JSONObject obj = treeFragmentToJSON(aItem);
            if(selected!=null && selected.getId().equals(aItem.getId())){
//                int pos = aItem.getParent().children.indexOf(aItem);
                if(autoMoveFocus){
                if(pos>=aItem.getParent().getChildrenCount()){
                    pos = aItem.getParent().getChildrenCount()-1;
                }
                String id = "";
                if(pos<0){
                    if(!aItem.getParent().equals(root))
                        id = aItem.getParent().getId();
                }else{
                    TreeControllerItem item =
                            (TreeControllerItem) aItem.getParent().children.get(pos);
                    id = item.getId();
                }
                obj.put("selectID", id);
                }else{
                    setSelectedItem(null, false);
                }
            }
            frame.sendMessage("treeItemRemoved", obj);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    void itemModified(TreeControllerItem aItem){
        if(silentMode)
            return;
        try {
            frame.sendMessage("treeItemModified", treeItemToJSON(aItem));
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    public TreeControllerItemProvider getItemProvider() {
        return itemProvider;
    }

    public void setItemProvider(TreeControllerItemProvider itemProvider) {
        this.itemProvider = itemProvider;
    }

    public TreeControllerItem<Object, T> getRoot() {
        return root;
    }

    public TreeControllerItem getSelected() {
        return selected;
    }

    public Frame getFrame() {
        return frame;
    }
    
    public JSONObject getTreeH(JSONObject aData){
//        Log.debug(this, "getTreeH");
        return getTree();
    }

    public void selectionChangedH(JSONObject aData){
        selectionChanged(aData.optString("id", ""));
    }

    public void setStateH(JSONObject aData){
        setState(aData.optString("id", ""), aData.optBoolean("state"));
    }

    /**
     * @return the autoMoveFocus
     */
    public boolean isAutoMoveFocus() {
        return autoMoveFocus;
    }

    /**
     * @param autoMoveFocus the autoMoveFocus to set
     */
    public void setAutoMoveFocus(boolean autoMoveFocus) {
        this.autoMoveFocus = autoMoveFocus;
    }
}
